package tests;

import java.util.Random;
import java.util.Vector;

import org.lwjgl.openal.AL;
import org.lwjgl.openal.AL10;
import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.BasicGame;
import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.Input;
import org.newdawn.slick.ShapeFill;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.Sound;
import org.newdawn.slick.fills.GradientFill;
import org.newdawn.slick.geom.Circle;
import org.newdawn.slick.geom.Curve;
import org.newdawn.slick.geom.GeomUtil;
import org.newdawn.slick.geom.Line;
import org.newdawn.slick.geom.Polygon;
import org.newdawn.slick.geom.Rectangle;
import org.newdawn.slick.geom.Shape;
import org.newdawn.slick.geom.Vector2f;
import org.newdawn.slick.geom.GeomUtil.HitResult;
import org.newdawn.slick.openal.SoundStore;

 
public class PlanetTest extends BasicGame {
	/** True if we're clipping an area */
	private boolean clip;
	/** The angle of rotation */
	private float ang;
	/** The image being rendered */
	private Image ground;
	/** A polygon to be rendered */
	private Polygon poly;
	/** The container holding this test */
	private GameContainer container;
	
	private boolean renderSky = false;
	private boolean renderGroundTexture = false;
	
	Random random = new Random();
	
	Image falf_1;
	Image falf_2; 
	Image sky;
	
	Shape mousePointer;
	private GeomUtil util = new GeomUtil();
	
	
	/**
	 * Create a new test of graphics context rendering
	 */
	int original_width;
	int original_height;
	private float angle;
	private float[] pts = new float[50];

	private int nbOfPoints;
	private int xp;
	private int yp;
	private Line line;
	private HitResult hitResult;
	private Player player;
	
	public PlanetTest() {
		super("-");
	}
	
	/**
	 * @see org.newdawn.slick.BasicGame#init(org.newdawn.slick.GameContainer)
	 */
	public void init(GameContainer container) throws SlickException {
		this.container = container;
		ground = new Image("data/test/planet.png");
		original_width = ground.getWidth();
		original_height = ground.getHeight();
		ground.setAlpha(1f);
		ground = generateRandomFractalTerrain(container);
			//generateImage2(container); 
			//generateRandomFractalTerrain(container);
		sky = generateSky(container);
		mousePointer = new Circle(container.getWidth()/2, container.getHeight()/2, 5f);
		player = new Player(new Vector2f(1,1));
		SoundStore.get().init();
		AL10.alGetError();
		Sound snd = new Sound("data/sound/mus.ogg");
		snd.play();
	}
	
	/**
	 * http://slick.cokeandcode.com/wiki/doku.php?id=generer_une_image
	 * http://freespace.virgin.net/hugo.elias/models/m_landsp.htm
	 * @throws SlickException
	 */
	public void generateImage() throws SlickException{
		Image localImg = new Image(256,256);
		Graphics graphic = localImg.getGraphics();
		graphic.clear();
		int factor;
		
		int angle;
		int h;
		for(int i = 0; i<100; i++) {
			factor = Math.round(original_height/(1+random.nextInt(10))); // cannot have 0
			System.out.println("pass number :"+i+" factor:"+factor);
			falf_1 = ground.getSubImage(0, 0, original_width, factor);
			falf_2 = ground.getSubImage(0, factor, original_width, original_height-factor);
			
			System.out.println("0,0,"+original_width+","+factor);
			System.out.println("0,"+factor+","+original_width+","+(original_height-factor));
//			
			if(random.nextBoolean()){
				falf_1 = falf_1.getScaledCopy(1-0.1f*random.nextInt(2));				
				falf_2 = falf_2.getScaledCopy(1+0.1f*random.nextInt(2));
			} else {
				falf_1 = falf_1.getScaledCopy(1+0.1f*random.nextInt(2));
				falf_2 = falf_2.getScaledCopy(1-0.1f*random.nextInt(2));
			}
			h = falf_1.getHeight(); // height of the first image
			angle = random.nextInt(360); // random angle
			System.out.println("h:"+h+" angle:"+angle);
//			falf_1.rotate(angle);
//			falf_2.rotate(angle);
			graphic.drawImage(falf_1, 0, 0);
			graphic.drawImage(falf_2, 0, h);
			graphic.flush();
			ground = localImg;
			ground.rotate(angle);
		}
		System.out.println(" done!");
	}
//	
//	public void generateImage3(GameContainer c) throws SlickException{
//		Image localImg = new Image(c.getWidth(), c.getHeight());
//		Graphics graphic = localImg.getGraphics();
//		graphic.clear();
//		graphic.setColor(Color.blue);
//		graphic.fillArc(100, 400, 800, 500, 180, 360);
//		graphic.flush();
//		ground = localImg;
//		
//	}
	
//	public void generateDiscWord(GameContainer c) throws SlickException{
//		Image localImg = new Image(c.getWidth(), c.getHeight());
//		Graphics graphic = localImg.getGraphics();
//		graphic.clear();
//		graphic.setColor(Color.blue);
//		int nbPoints = 45;
//		this.pts = getPoints(c.getWidth()/2, c.getHeight()/2, 350, nbPoints, 10, 300, "clockwise", true); 
//				//{0,702, 0,700, 50,70, 60,80, 90,20, 100,50, 110,80, 120,90, 150, 400, 210,700, 210,702};
//		Shape shape = new Polygon(pts);
//		ShapeFill gradient2 = new GradientFill(0,-75,Color.green,0,75,Color.black,true);
//		ShapeFill gradient = new GradientFill(0,0,Color.green,0,400,Color.black,true);
//		graphic.draw(shape,gradient2);
//		graphic.fill(shape,gradient);
//		graphic.flush();
//		ground = localImg;
//		System.out.println("done!");
//	}
	
	public class Segment {
		Vector2f pointA;
		Vector2f pointB;
		public Segment(final Vector2f pointA, final Vector2f pointB) {
			this.pointA = pointA.copy();
			this.pointB = pointB.copy();
		}
		public Vector2f getPointA(){
			return pointA.copy();
		}
		public Vector2f getPointB(){
			return pointB.copy();
		}
		
		public String toString(){
			return pointA+".---."+pointB;
		}
	}
	/**
	 * FRACTAL
	 * @param c
	 * @return
	 * @throws SlickException
	 */
	public Image generateRandomFractalTerrain(GameContainer c) throws SlickException{
		Image localImg = new Image(c.getWidth(), c.getHeight());
		Graphics graphic = localImg.getGraphics();
		graphic.clear();
		graphic.setColor(Color.white);
		int nbPasses = 5;
		int variationOnY = 75;
		int variationOnYByPass = variationOnY / nbPasses;
		
		Vector<Segment> segments = new Vector<Segment>();
		Vector2f start = new Vector2f(0, 0.6f*c.getHeight());
		Vector2f end = new Vector2f(c.getWidth(), 0.8f*c.getHeight());
		segments.add(new Segment(start, end));

		Vector<Segment> segments_;
		Vector2f midd;
		for(int i = 0; i<nbPasses; i++){
			segments_ = new Vector<Segment>(segments.size()*2); 
			for(Segment s : segments){
				System.out.println("s="+s);
				midd = s.getPointA().add(s.getPointB().sub(s.getPointA()).scale(0.5f));
				if(random.nextBoolean())
					midd.y+=random.nextInt(variationOnY - i*variationOnYByPass);
				else 
					midd.y-=random.nextInt(variationOnY - i*variationOnYByPass);
				System.out.println("number of pass :"+i+" A:"+s.getPointA()+" B:"+s.getPointB()+" = midd"+midd);
				segments_.add(new Segment(s.getPointA(), midd));
				segments_.add(new Segment(midd, s.getPointB()));
			}
			segments = segments_;
		}
		System.out.println("---------------------------------");
		for(Segment s : segments){
			System.out.println(s);
		}
		
		this.pts = convertVectorSegment2floatArray(segments, c);
//		float[] d = {0,702, 0,700, 50,70, 60,80, 90,20, 100,50, 150,80, 220,90, 350, 400, 1000,700, 1000,702};
		//Shape shape = new Polygon(d);
		poly = new Polygon(pts);
		if(renderGroundTexture) {
		Image image1 = new Image("data/test/grass.png");
		Image image2 = new Image("data/test/rocks.png");
		ShapeFill gradient = new GradientFill(10,0,new Color(1,1,1,1f),10,10,new Color(1,1,1,1), true); 
		graphic.texture(poly, image2);
		graphic.texture(poly, image1, gradient);
		} else {
			graphic.draw(poly);
		}
		//graphic.texture(shape, new Image("data/test/groundTexture.png"));
		graphic.flush();
		System.out.println("done!");
		return localImg;
	}
	
	private float[] convertVectorSegment2floatArray(Vector<Segment> segments, GameContainer container){
		float[] segs = new float[segments.size()*2 + 6];
		int i = 0;
		Vector2f ss = null;
		for(Segment s : segments){
			segs[i] = s.getPointA().x;
			segs[i+1] = s.getPointA().y;
//			segs[i+2] = s.getPointB().x;
//			segs[i+3] = s.getPointB().y;
			i+=2;
			ss = s.getPointB();
		}
		segs[segs.length-6] = ss.x;
		segs[segs.length-5] = ss.y;
		segs[segs.length-4] = container.getWidth();
		segs[segs.length-3] = container.getHeight();
		segs[segs.length-2] = 0;
		segs[segs.length-1] = container.getHeight();
		
		nbOfPoints = segs.length/2;
		return segs;
	}
	
	public Image generateSky(GameContainer c) throws SlickException {
		Image localImg = new Image(c.getWidth(), c.getHeight());
		Graphics graphic = localImg.getGraphics();
		graphic.clear();
		graphic.setColor(Color.white);
		Shape shape = new Rectangle(0,0,c.getWidth(),c.getHeight());
		ShapeFill gradient = new GradientFill(0,-1,Color.blue,0,1,Color.white,true);
		graphic.fill(shape,gradient);
		graphic.flush();
		return localImg;
	}
	
	/**
	 * http://www.gameprogrammer.com/fractal.html
	 * @param c
	 * @return
	 * @throws SlickException
	 */
	public Image generateImage2(GameContainer c) throws SlickException {
		Image localImg = new Image(c.getWidth(), c.getHeight());
		Graphics graphic = localImg.getGraphics();
		graphic.clear();
		graphic.setColor(Color.white);
		Shape shape = new Curve(new Vector2f(0,400), new Vector2f(c.getWidth()/4,100), new Vector2f((3/4)*c.getWidth(),550), new Vector2f(c.getWidth(),400), 15);
		pts = shape.getPoints();
		float[] pts_ = new float[pts.length+4];
		for(int i = 1; i<pts.length; i++){
			pts_[i] = pts[i];
		}
		pts_[pts_.length-4] = c.getWidth();
		pts_[pts_.length-3] = c.getHeight();
		pts_[pts_.length-2] = 0;
		pts_[pts_.length-1] = c.getHeight();
		shape = new Polygon(pts_);
		graphic.texture(shape, new Image("data/test/groundTexture.png"));
//		ShapeFill gradient2 = new GradientFill(0,-75,Color.green,0,75,Color.black,true);
//		ShapeFill gradient = new GradientFill(0,0,Color.green,10,3,Color.black,true);
//		graphic.draw(shape,gradient2);
//		graphic.fill(shape,gradient);
		graphic.flush();
		System.out.println("done!");
		return localImg;
	}
	
	
	
	/**
	 * "clockwise"
	 */
	private float[] getPoints(double centerx, double centery, double circleradius, int totalpoints, int startangle, int arc ,String pointdirection, boolean evendistribution) {
		
		double mpi = Math.PI/180;
		double startRadians = startangle * mpi;
		
		double incrementAngle = arc/totalpoints;
		double incrementRadians = incrementAngle * mpi;
		
		if(arc<360) {
			// this spreads the points out evenly across the arc
			if(evendistribution) {
				incrementAngle = arc/(totalpoints-1);
				incrementRadians = incrementAngle * mpi;
			} else {
				incrementAngle = arc/totalpoints;
				incrementRadians = incrementAngle * mpi;
			}
		}
		
		double xp;
		double yp;
		//Vector<Double> pts_ = new Vector<Double>(2*totalpoints);
		float[] pts = new float[2*totalpoints];
		for(int i = 0; i<2*totalpoints;i+=2) {
			 xp = centerx + Math.sin(startRadians) * circleradius;
			 yp = centery + Math.cos(startRadians) * circleradius;
			//pt:Point = new Point(xp, yp);
			 pts[i] = (float) xp;
			 pts[i+1] = (float) yp;

			if(pointdirection=="clockwise") {
				startRadians += incrementRadians;
			} else {
				startRadians -= incrementRadians;
			}
		}
		return pts;
	}
	
private Vector<Segment> getSegmentAroundCercle(double centerx, double centery, double circleradius, int totalpoints, int startangle, int arc ,String pointdirection, boolean evendistribution) {
		
		double mpi = Math.PI/180;
		double startRadians = startangle * mpi;
		
		double incrementAngle = arc/totalpoints;
		double incrementRadians = incrementAngle * mpi;
		
		if(arc<360) {
			// this spreads the points out evenly across the arc
			if(evendistribution) {
				incrementAngle = arc/(totalpoints-1);
				incrementRadians = incrementAngle * mpi;
			} else {
				incrementAngle = arc/totalpoints;
				incrementRadians = incrementAngle * mpi;
			}
		}
		
		double xp;
		double yp;
		//Vector<Double> pts_ = new Vector<Double>(2*totalpoints);
		float[] pts = new float[2*totalpoints];
		for(int i = 0; i<2*totalpoints;i+=2) {
			 xp = centerx + Math.sin(startRadians) * circleradius;
			 yp = centery + Math.cos(startRadians) * circleradius;
			//pt:Point = new Point(xp, yp);
			 pts[i] = (float) xp;
			 pts[i+1] = (float) yp;

			if(pointdirection=="clockwise") {
				startRadians += incrementRadians;
			} else {
				startRadians -= incrementRadians;
			}
		}
		Vector<Segment> segments = new Vector<Segment>(pts.length-1);
		for(int i = 0; i<pts.length-4; i+=2) {
			// {Ax, Ay, Bx,By, Cx, Xy}
			segments.add(new Segment(new Vector2f(pts[i],pts[i+1]), new Vector2f(pts[i+2],pts[i+3])));
		}
		return segments;
	}
 
	

	/**
	 * @see org.newdawn.slick.BasicGame#render(org.newdawn.slick.GameContainer, org.newdawn.slick.Graphics)
	 */
	public void render(GameContainer container, Graphics g) throws SlickException {
		if(renderSky)
		g.drawImage(sky,0,0);
		
		g.drawImage(ground, 0,0 );
		g.drawString("nbOfPoints:"+nbOfPoints, 0, 40);
		g.draw(mousePointer);
		if(line!=null)
		g.draw(line);
		player.render(container, g);
		g.drawString("angle:"+angle, 100, 10);
//		if(falf_1!=null)
//		g.drawImage(falf_1, 0,0 );
//		if(falf_2!=null)
//		g.drawImage(falf_2, 256,256 );
	}

	/**
	 * @see org.newdawn.slick.BasicGame#update(org.newdawn.slick.GameContainer, int)
	 */
	public void update(GameContainer container, int delta) {
		// image.rotate(delta * angle);
		xp = container.getInput().getMouseX();
		yp = container.getInput().getMouseY();
		
		line = new Line(new Vector2f(xp,yp), new Vector2f(xp,container.getHeight()));
		hitResult = util.intersect(poly, line);
		mousePointer.setCenterX(xp);
		if(hitResult!=null && hitResult.pt!=null)
			mousePointer.setCenterY(hitResult.pt.y);
		else {
			mousePointer.setCenterY(yp);
		}
		player.update(container, delta);
	}

	/**
	 * @see org.newdawn.slick.BasicGame#keyPressed(int, char)
	 */
	public void keyPressed(int key, char c) {
		if (key == Input.KEY_ESCAPE) {
			SoundStore.get().clear();
			AL.destroy();
			System.out.println("--");
			try {
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("-exit-");
			System.exit(0);
		}
		if (key == Input.KEY_SPACE) {
			if(angle == 360) {
				angle = 0;
			} else {
				angle+=0.1f;
			}
		}
		player.keyPressed(key, c);
	}
	
	/**
	 * Entry point to our test
	 * 
	 * @param argv The arguments passed to the test
	 */
	public static void main(String[] argv) {
		try {
			AppGameContainer container = new AppGameContainer(new PlanetTest());
			container.setDisplayMode(200,200,false);
			container.start();
			
		} catch (SlickException e) {
			e.printStackTrace();
		}
	}
	
	public class Player{
		private Shape shape;
		private Line line;
		private int MaxScreenWidth;
		private ShapeFill gradient;
		private boolean jump;
		private float speed = 0;
		private HitResult hitResult;
		private float addedY =0;
		
		
		public Player(Vector2f respawnPosition){
			shape = new Rectangle(respawnPosition.x, respawnPosition.y, 5, 20);
			gradient = new GradientFill(0,-75,Color.green,0,75,Color.blue,true);
		}
		public Vector2f getPosition() {
			return new Vector2f(shape.getX(), shape.getY());
		}
		
		public Shape getShape() {
			return shape;
		}
		 
		public void render(GameContainer container, Graphics g) throws SlickException {
			g.setColor(Color.red);
			g.fill(shape,gradient);
			g.draw(shape);
			g.setColor(Color.green);
			g.drawString("pos:"+shape.getX()+","+shape.getY()+" hitResult.y:"+(hitResult!=null?hitResult.pt.y:""), shape.getX()+25, shape.getY()-50);
			g.setColor(Color.yellow);
			if(line!=null)
			g.draw(line);
			g.setColor(Color.white);
		}
		
		public void update(GameContainer container, int delta) {
			MaxScreenWidth = container.getWidth();
			if(jump){
				speed-=1*delta;
				addedY +=1*delta;
				if(speed<=0) {
					addedY = 0;
					jump = false;
				}
				shape.setY(shape.getY()-addedY*delta);
			} 
			line = new Line(new Vector2f(shape.getX(),shape.getY()), new Vector2f(shape.getX(),container.getHeight()));
			hitResult = util.intersect(poly, line);
			if(hitResult!=null && hitResult.pt!=null) {
				shape.setX(hitResult.pt.x);
				if(hitResult.pt.y-shape.getHeight()>shape.getY()) {
					shape.setY(shape.getY()+0.1f * delta);
				} else {
					shape.setY(hitResult.pt.y-shape.getHeight());

				}
			} else {
				shape.setY(100);
			}
			if (container.getInput().isKeyDown(Input.KEY_LEFT)) {
				System.out.println("<-");
				if(shape.getX()>0)
					shape.setX(shape.getX()-0.1f*delta);
				else
					shape.setX(0);
			}
			if (container.getInput().isKeyDown(Input.KEY_RIGHT)) {
				System.out.println("->");
				if(shape.getX()<MaxScreenWidth)
					shape.setX(shape.getX()+0.1f*delta);
				else
					shape.setX(MaxScreenWidth); 
			}
		}
		 
		public void keyPressed(int key, char c) {
			if (key == Input.KEY_SPACE && !jump) {
				System.out.println("jump!");
				jump = true;
				speed = 15;
			}
//			if (key == Input.KEY_RIGHT) {
//				
//			}
		}
		
	}
}